package gbench.sandbox.matrix.img;

import org.jblas.DoubleMatrix;
import org.junit.jupiter.api.Test;

import java.awt.Color;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.fs.XlsFile.DataMatrix;
import gbench.sandbox.matrix.img.util.BlasMatrix;
import gbench.sandbox.matrix.img.util.MatlibCanvas;
import gbench.sandbox.matrix.img.util.MatlibCanvas.NVec;

import static java.lang.Math.*;
import static gbench.sandbox.matrix.img.util.ImageOps.*;
import static gbench.sandbox.matrix.img.util.MatlibCanvas.*;
import static gbench.common.tree.LittleTree.*;
import static gbench.common.tree.LittleTree.IRecord.*;

/**
 * 绘图 :点集合的变换b
 * @author gbench
 *
 */
public class JunitMatlibTransform2 {
    
    /**
     * 绘图 <br>
     * 注意：此处的 线 除非特别说明，一般指 曲线，不一定 是直线，但也可以是 直线，即 一般意义上的 线。
     * @param size 窗口大小
     * @param plotflag 绘图标记，是否打开 图形窗口，true:打开,false:不打开
     */
    public void plot(final Integer size,final Boolean plotflag) {
        
        final var matlib = new MatlibCanvas(size); // 数学绘图（函数式的描述曲线）画布 组件
        
        matlib.plot(size, size, pen -> {
            
            final var theta = PI/5; // 旋转的角度
            final var r = 100; // 半径
            final var centerx = 250; // 圆心x
            final var centery = r; // 圆心y
            
            //直线旋转
            pen.drawLine(x->100d);
            pen.setColor(clr2rgb(Color.BLUE));
            pen.drawLine(x->100d,points->rotateMx(PI/5).mmult(points));
            
            // 旋转前的椭圆
            pen.setColor(clr2rgb(Color.CYAN)); // 修改默认颜色为 蓝绿色即青色
            pen.drawStraightLine(0, 0, centerx, centery);
            pen.drawPoints(range2pi(5000).map(matlib::rhoTheta)
                .map(e->e.affine(r,r/3,centerx,centery)));// 水平
            pen.setColor(clr2rgb(Color.YELLOW)); // 修改默认颜色为 蓝绿色即青色
            
            // 旋转后的椭圆
            pen.drawPoints( range2pi(5000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),
                // 旋转 theta角度
                rotate(theta));// 旋转
            
            final var center_new = rotate(theta).apply(xyZero(250,100)).corece(e->e.intValue());// 旋转后的圆心,新的圆心
            pen.drawStraightLine(0, 0, center_new.get(0),  center_new.get(1));
            
            // 旋转+平移
            pen.setColor(clr2rgb(Color.RED)); // 修改默认颜色为 蓝绿色即青色
            final var points_or_points2_flag = false; // 旋转平移的 方案
            //椭圆的旋转与平移
            if(points_or_points2_flag)
                pen.drawPoints( range2pi(5000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),
                    // 对于要做 整个椭圆的旋转  操作,移动到原点进行旋转，这一点很关键。旋转之后再进行 逆向平移，这里的次序不能乱，要用心体会。
                    px -> rotateMx(PI/2).mmult(px.add(xyZero(-centerx,-centery))) // 先移动到原点，然后旋转。
                        .add(xyZero(centerx,centery) // 然后在移动到新的坐标位置
                ));// drawPoints
            else // 使用 drawPoints2 绘制 
                pen.drawPoints2( range2pi(1000000).map(matlib::rhoTheta).map(e->e.affine(r,r/3,centerx,centery)),NVec::toArray2,
                     // 对于要做 整个椭圆的旋转  操作,移动到原点进行旋转，这一点很关键。旋转之后再进行 逆向平移，这里的次序不能乱，要用心体会。
                      px -> {
                            final var ar = new AtomicReference<List<int[]>>(); // 闭包外空间,用作一个 可以修改值的 Optional 对象。
                            final var blas_flag = true; // 是否启用 blas,blas 启动需要时间,单次使用消耗时间长,单多次重复使用消耗低
                            final var times = 10; // 重复使用的时间消耗
                            final var total = Stream.iterate(0d,i->i<times,i->i+1d).map( i -> // 统计每次变换的时间消耗
                                timeit( () -> {
                                    if(blas_flag) {
                                        final var mx = BlasMatrix.of(px).transpose();
                                        final var y = xyZero(-centerx,-centery,BlasMatrix::of);
                                        final var _y = y.mul(-1d);
                                        final var mm = rotateMx((i+1)*PI/times+1,BlasMatrix::of).mmul(mx.addColumnVector(y)).addColumnVector(_y);
                                        final var ll = BlasMatrix.list(mm.transpose(),compose_f(NVec::ints,NVec::new)); // 旋转后的节点序列
                                        
                                    // 更新并 压缩数据
                                    ar.updateAndGet( pre -> pre != null // 如果存在先前值，则把ll与先前值按照x,y坐标进行分组合并并去重，以减少数据冗余量
                                        ? new ArrayList<>( GROUP2MAP(Stream.of(pre, ll).flatMap(e -> e.stream()), e -> e[0] + "/" + e[1]).values())// 去除重复
                                        : ll ); // updateAndGet
                                        
                                    }else { // 与blas 约有100多倍的 差距,DataMatrix 在做大规模 运算的使用 垃圾的一塌糊涂。
                                        final var mx = DataMatrix.of(px,true).transpose();
                                        final var mm = rotateMx(PI/2).mmult(mx.add(xyZero(-centerx,-centery))).add(xyZero(centerx,centery));
                                        final var ll = mm.colS().map(NVec::new).map(NVec::ints).collect(Collectors.toList());
                                        ar.set(ll); // 设置返回结果
                                    }// if blas_flag
                                } ) // timeit
                            ).collect(Collectors.summingDouble(e->e));
                            
                            // 显示累积历时时间消耗
                            System.out.println("total last for:"+total+" ms,\tavg:"+total/times+"ms");
                            
                            // 返回结果
                            timeit(()->{
                                System.err.println(" total size:"+ar.get().size());
                            });
                            
                            return ar.get();
                      }// px
                  );// drawPoints2
            
        },plotflag); // plot
    }
    
    @Test
    public void bar() {
        // 绘制图形
        plot(size,plotflag);
        
        // 生成一个2PI[-PI,PI]周期中的  6个采样数据点
        range2pi(6-1).forEach((i,d)->System.out.println(i+"#\t"+d));
    }
    
    /**
     * 维度空间的生成
     */
    @Test
    public void qux() {
        final var rng = Range.of(2);// 0,1
        
        // 以rng为轴 依次 逐个 调用 衍生化 维度 张成器 以渐进的方式 做维度递增操作，进而构造多维空间，此处是4维
        rng.flatMap(rng.derived_2d_ctor()) // 1维->2维 扩增空间
           .flatMap(rng.derived_nd_ctor()) // 2维->3维 扩增空间
           .flatMap(rng.derived_nd_ctor()) // 3维->4维 扩增空间
           .map(kvp()).forEach(e->{ // 映射 增加 索引序号，并给予遍历
                System.out.println(e._1()+"#"+LIST(e._2()));
           });
        
        System.out.println("\nspan 无需逐次递增维度，一次性就可以张成多维空间");
        // 以rng为轴 一次性 调用 多个 衍生化 维度 张成器 ，张成 多维空间，此处是4维
        rng.span(rng.derived_2d_ctor(),rng.derived_nd_ctor(),Range.of(3).derived_nd_ctor())
           .map(kvp()).forEach(e->{ // 映射 增加 索引序号，并给予遍历
                System.out.println(e._1()+"-->"+LIST(e._2()));
           });
    }
    
    @Test
    public void qux1() {
        final var mx = DoubleMatrix.linspace(0, 10, 11);
        final var mx2 = DoubleMatrix.concatHorizontally(mx, mx);
        
        System.out.println("BlasMatirx.list");
        BlasMatrix.list(mx,compose_f(Arrays::asList,BlasMatrix::box)).forEach(System.out::println);
        System.out.println("BlasMatirx.list2");
        BlasMatrix.list2(mx,compose_f(Arrays::asList,BlasMatrix::box)).forEach(System.out::println);
        
        System.out.println("BlasMatrix.dmx");
        System.out.println(BlasMatrix.dmx(mx));
        
        System.out.println("XVec:视二维矩阵为元素为1维数组的的列向量");
        final var xvec = XVec.of(mx2.toArray2());// 视二维矩阵为1元素为数组的的列向量
        final var bb = xvec.mutate(DoubleMatrix::new);
        System.out.println((bb+"").replace(";", "\n"));
        
        System.out.println();
        final var cc = XVec.of(A(1,2,3));
        System.out.println("derive2");
        System.out.println(cc.derive2(e->e.intValue()));
        System.out.println("duplicate");
        System.out.println(cc.duplicate());
        
        System.out.println("BlasMatrix.list");
        BlasMatrix.list(mx,compose_f(Arrays::asList,BlasMatrix::box)).forEach(System.out::println);
    }
    
    /**
     * cph 的展示
     */
    @Test
    public void qux2() {
        System.out.println("\nRange.cph(2,3,2,2)");
        Range.cph(2,3,2,2).forEach(e->{
            System.out.println(NVec.of(e).intL());
        });
        
        System.out.println("\nRange.cph(2,2)");
        Range.cph(2,2).forEach(e->{
            System.out.println(NVec.of(e).intL());
        });
        
        System.out.println("\nhead(1,2,3):"+NVec.of(1,2,3).bighead());
        System.out.println("tail(1,2,3):"+NVec.of(1,2,3).bigtail());
    }
    
    /**
     * reshape 的展示
     */
    @Test
    public void qux3() {
        
        final var oo3 = (Double[][][])NVec.of(1,2,3,4,5,6,7,8).reshape(2,2,2);
        Function<Object,String> formatter = obj->(obj instanceof Object[]? Arrays.asList((Object[])obj):obj)+"";
        System.out.println(NVec.of("00003"));
        System.out.println(DataMatrix.of(oo3).toString(formatter));
        
        final var oo2 = (Double[][])NVec.of(1,2,3,4,5,6,7,8).reshape(2,2);
        System.out.println(NVec.of("002"));
        System.out.println(DataMatrix.of(oo2));
        
        
        final var oo1 = (Double[])NVec.of(1,2,3,4,5,6,7,8).reshape(10);
        System.out.println(NVec.of("001"));
        System.out.println(NVec.of(oo1));
        
        System.out.println("-----------------------------");
        
        final var _oo1 = NVec.of(1,2,3,4,5,6,7,8).reshape1(10);
        System.out.println("_001");
        System.out.println(Arrays.deepToString(_oo1));
        
        final var _oo2 = NVec.of(Range.of(4)).reshape2(2,2);
        System.out.println("_002");
        System.out.println(Arrays.deepToString(_oo2));
        
        final var _oo3 = NVec.of(Range.of(8)).reshape3(2,2,2);
        System.out.println("_003");
        System.out.println(Arrays.deepToString(_oo3));
        
        final var _oo4 = NVec.of(Range.of(16)).reshape4(2,2,2,2);
        System.out.println("_004");
        System.out.println(Arrays.deepToString(_oo4));
        
        final var _oo5 = NVec.of(Range.of(32)).reshape5(2,2,2,2,2);
        System.out.println("_005");
        System.out.println(Arrays.deepToString(_oo5));
        
        final var _oo6 = NVec.of(Range.of(64)).reshape6(2,2,2,2,2,2);
        System.out.println("_006");
        System.out.println(Arrays.deepToString(_oo6));
    }
    
    /**
     *  >> 和   >>>
     *  >>> 会把 负数变成 整数，但 >>不会。 即 >> 会根据 符号为来决定 补填 1还是补填0, >>> 不管正负一律填0
     */
    @Test
    public void qux4() {
        System.err.println("8右移动>>>:"+(8>>>1));
        System.err.println("8右移动:>>"+(8>>1));
        System.err.println("-8右移动>>>:"+(-8>>>1));
        System.err.println("-8右移动:>>"+(-8>>1));
    }

    public Integer size=500; // 图片尺寸单位
    public Boolean plotflag=false; // 绘图标记
    
    /**
     * 
     * @param args
     */
    public static void main(String args[]) {
        final var jmlc = new JunitMatlibTransform2();
        jmlc.plot(jmlc.size, true);
    }
    
}
